﻿using Microsoft.Win32;
using OpenCvSharp;
using OpenCvSharp.WpfExtensions;
using OpenCVWPF.ViewModel;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Numerics;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Navigation;
using System.Windows.Shapes;
using static System.Net.Mime.MediaTypeNames;
using Window = System.Windows.Window;

namespace OpenCVWPF
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        MainWindowViewModel ViewModel = new MainWindowViewModel();

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = ViewModel;
        }

        /// <summary>
        /// 打开图像按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OpenImage(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName;
                var mat = Cv2.ImRead(imgPath); // 默认读取3通道的彩色图片
                if (!mat.Empty())
                {
                    ViewModel.MainImage = mat.ToBitmapSource(); // 显示图像
                }
                else
                {
                    MessageBox.Show("没有读取到图像！");
                }
            }
        }

        /// <summary>
        /// 播放视频文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnPlayVideo(object sender, RoutedEventArgs e)
        {
            // 创建VideoCapture对象并打开视频文件
            using var capture = new VideoCapture(@"Resources/点云应用.mp4");
            // 检查视频是否成功打开
            if (capture.IsOpened())
            {
                double framefps = capture.Get(VideoCaptureProperties.Fps); // 获取视频每秒帧率
                double frameWidth = capture.Get(VideoCaptureProperties.FrameWidth); // 获取视频帧宽度
                double frameHeight = capture.Get(VideoCaptureProperties.FrameHeight); // 获取视频帧高度
                double framePosMsec = capture.Get(VideoCaptureProperties.PosMsec); // 获取视频帧的当前位置（以毫秒为单位）
                double framePosFrames = capture.Get(VideoCaptureProperties.PosFrames); // 接下来要捕获的帧的相对位置（从 0 开始，例如 - 0、1、2、3 等）
                double frameFourcc = capture.Get(VideoCaptureProperties.FourCC); // 编解码器的4个字符的代码
                double frameCount = capture.Get(VideoCaptureProperties.FrameCount); // 视频文件中的总帧数
                Console.WriteLine($"每秒帧率:{framefps}");
                var playLocation = frameCount / 2; // 计算视频从中间播放
                capture.Set(VideoCaptureProperties.PosFrames, playLocation);  // 设置视频从中间播放
                while (true)
                {
                    // 读取新的帧
                    Mat frame = new Mat();
                    bool isSuccess = capture.Read(frame); // 读取新的帧

                    // 读取视频帧失败(视频播放完毕)，视频播放停止 
                    if (!isSuccess)
                    {
                        Console.WriteLine("读取视频帧失败，视频播放停止!");
                        break;
                    }
                    // 如果帧为空，意味着视频已经播放完毕
                    if (frame.Empty())
                    {
                        Console.WriteLine("视频已经播放完毕!");
                        break;
                    }


                    // 显示视频帧
                    Cv2.ImShow("播放视频", frame);

                    // 等待键盘输入，如果按下'ESC'键退出循环
                    int key = Cv2.WaitKey(20);
                    if (key == 27) // 'ESC' key
                    {
                        Console.WriteLine("按下了ESC建，停止播放视频!");
                        break;
                    }
                }
            }

            // 释放资源和关闭窗口
            Cv2.DestroyAllWindows();
        }

        /// <summary>
        /// 播放视频文件2
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnPlayVideo2(object sender, RoutedEventArgs e)
        {
            // 创建VideoCapture对象并打开视频文件
            using var capture = new VideoCapture(@"Resources/点云应用.mp4");
            // 检查视频是否成功打开
            if (capture.IsOpened())
            {
                //capture.Set(VideoCaptureProperties.PosFrames, 0);  // 设置视频播放
                while (true)
                {
                    // 读取新的帧
                    Mat frame = new Mat();
                    bool isSuccess = capture.Read(frame); // 读取新的帧

                    // 读取视频帧失败(视频播放完毕)，视频播放停止 
                    if (!isSuccess)
                    {
                        Console.WriteLine("读取视频帧失败，视频播放停止!");
                        break;
                    }
                    // 如果帧为空，意味着视频已经播放完毕
                    if (frame.Empty())
                    {
                        Console.WriteLine("视频已经播放完毕!");
                        break;
                    }


                    // 显示视频帧
                    ViewModel.MainImage = frame.ToBitmapSource(); // 显示视频帧

                    // 等待键盘输入，如果按下'ESC'键退出循环
                    int key = Cv2.WaitKey(20);
                    if (key == 27) // 'ESC' key
                    {
                        Console.WriteLine("按下了ESC建，停止播放视频!");
                        break;
                    }
                }
            }
        }

        /// <summary>
        /// 从相机播放视频
        /// 这段代码会打开默认摄像头，捕获视频，并在窗口中实时显示。当按下任何键时，程序将退出。请确保你的摄像头正常工作并且你的应用有权限访问摄像头。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCameraPlayingVideos(object sender, RoutedEventArgs e)
        {
            // 打开默认摄像头
            VideoCapture capture = new VideoCapture(0); // 根据索引打开电脑默认摄像头，如果有多个摄像头可以更改序号
            try
            {
                if (!capture.IsOpened()) // 判断摄像头是否打开
                {
                    MessageBox.Show("摄像头没有打开");
                    return;
                }

                //VideoCapture capture1 = new VideoCapture("Integrated Camera"); // 根据名称打开电脑摄像头
                //if (capture1.IsOpened()) // 判断摄像头是否打开
                //{
                //    MessageBox.Show("摄像头没有打开");
                //    return;
                //}

                double dWidth = capture.Get(VideoCaptureProperties.FrameWidth); // 获取视频帧的宽度
                double dHeight = capture.Get(VideoCaptureProperties.FrameHeight); // 获取帧的宽度

                Mat frame = new Mat();  // 图像
                while (true)
                {
                    // 从摄像头读取新的帧
                    capture.Read(frame);

                    // 如果帧为空，表示没有更多的视频帧可以读取
                    if (frame.Empty())
                    {
                        break;
                    }

                    // 在窗口中显示帧
                    Cv2.ImShow("Capture window", frame);

                    // 等待1毫秒，检查是否有按键事件
                    if (Cv2.WaitKey(1) >= 0)
                    {
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                // 释放资源
                capture.Dispose();
            }
        }

        /// <summary>
        /// 打开读取灰度图
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OpenGrayImage(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径
                var mat = Cv2.ImRead(imgPath, ImreadModes.Grayscale); // 读取单通道的灰度图
                if (!mat.Empty()) // 判空
                {
                    ViewModel.MainImage = mat.ToBitmapSource(); // 显示图像
                }
                else
                {
                    MessageBox.Show("没有读取到图像！");
                }
            }
        }

        /// <summary>
        /// 保存图像按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SaveImage(object sender, RoutedEventArgs e)
        {
            SaveFileDialog sfd = new SaveFileDialog(); // 保存对话框
            if (sfd.ShowDialog() is true && ViewModel.MainImage != null)
            {
                var imgPath = sfd.FileName; // 获取要保存的路径
                var mat = ViewModel.MainImage.ToMat(); // 转成Mat格式
                var isSuccess = Cv2.ImWrite(imgPath, mat);  // 保存图像
                if (isSuccess) // 判断图像是否保存成功
                    MessageBox.Show("图像保存成功");
                else
                    MessageBox.Show("图像保失败");
            }
        }

        /// <summary>
        /// 将相机视频保存到文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SaveVideo(object sender, RoutedEventArgs e)
        {
            // 打开默认摄像头
            VideoCapture capture = new VideoCapture(0); // 打开电脑默认摄像头，如果有多个摄像头可以更改序号
            try
            {
                if (!capture.IsOpened()) // 判断摄像头是否打开
                {
                    MessageBox.Show("摄像头没有打开");
                    return;
                }

                //VideoCapture capture = new VideoCapture("Integrated Camera"); // 根据名称打开电脑摄像头
                //if (capture.IsOpened()) // 判断摄像头是否打开
                //{
                //    MessageBox.Show("摄像头没有打开");
                //    return;
                //}

                double framefps = capture.Get(VideoCaptureProperties.Fps); // 获取视频每秒帧率
                if (framefps == 0)
                    framefps = 10;
                // 获取贞的分辨率
                var dWidth = (int)capture.Get(VideoCaptureProperties.FrameWidth); // 获取视频帧的宽度
                var dHeight = (int)capture.Get(VideoCaptureProperties.FrameHeight); // 获取帧的宽度

                OpenCvSharp.Size frameSize = new OpenCvSharp.Size(dWidth, dHeight); // 创建采集图像的大小
                int frames_per_second = (int)framefps; // 设置保存图像的贞率

                // 创建视频写入对象
                //string fileName, // 摄像头视频要保存的路径
                //FourCC fourcc, // 视频采集的编码格式 - 如果没有这种编码器，是会报错的。
                //double fps, // 视频要保持的帧率 - 这个帧率最好和摄像头的帧率保持一致
                //Size frameSize, // 视频要保存的大小
                //bool isColor = true
                VideoWriter videoWriter = new VideoWriter(@"Resources/MyVideo.avi", VideoWriter.FourCC('M', 'J', 'P', 'G'), frames_per_second, frameSize, true);
                if (videoWriter.IsOpened() == false) // 判断文件流打开失败推出
                {
                    MessageBox.Show("文件流打卡失败");
                    return;
                }
                Mat frame = new Mat(); // 从相机中读取采集的新的帧
                while (true) // 循环采集图像
                {
                    var isSuccess = capture.Read(frame); // 读取相机采集的帧
                    if (isSuccess == false)
                    {
                        break;
                    }
                    videoWriter.Write(frame); // 把采集当前帧写入到文件中
                    Cv2.ImShow("显示视频", frame); // 把当前帧显示到创建的窗口中

                    // 等待键盘输入，如果按下'ESC'键退出循环
                    int key = Cv2.WaitKey(20);
                    if (key == 27) // 'ESC' key
                    {
                        Console.WriteLine("按下了ESC建，停止播放视频!");
                        break;
                    }
                }
                // 必须释放使用的视频写入对象-c#中不手动释放应该也可以
                videoWriter.Release();
            }
            catch (Exception ex)
            {

                Console.WriteLine(ex.Message);
            }
            finally
            {
                // 释放资源
                capture.Dispose();
            }
        }

        /// <summary>
        /// 将相机视频保存到文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SaveVideo2(object sender, RoutedEventArgs e)
        {
            // 打开默认摄像头
            using var capture = new VideoCapture(0);

            // 获取摄像头的帧率
            double fps = capture.Get(VideoCaptureProperties.Fps);

            // 设置视频保存路径和编码格式
            string outputFile = "output.avi";
            FourCC fourCC = FourCC.XVID; // 可以尝试不同的编码格式
            OpenCvSharp.Size frameSize = new OpenCvSharp.Size((int)capture.Get(VideoCaptureProperties.FrameWidth), (int)capture.Get(VideoCaptureProperties.FrameHeight));

            // 创建VideoWriter对象
            using var writer = new VideoWriter(outputFile, fourCC, fps, frameSize, true);

            // 读取和写入帧
            Mat frame = new Mat();
            while (true)
            {
                if (!capture.Read(frame))
                {
                    Console.WriteLine("无法读取摄像头帧");
                    break;
                }

                // 写入帧到文件
                writer.Write(frame);

                // 显示帧（可选）
                Cv2.ImShow("Frame", frame);
                if (Cv2.WaitKey(25) >= 0)
                    break;
            }

            // 释放资源
            capture.Release();
            writer.Release();
            Cv2.DestroyAllWindows();
        }

        /// <summary>
        /// 显示图像
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ShowImage(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取mat图像
                Cv2.ImShow("Displayed Image", mat); // 显示图像
                Cv2.WaitKey(0); // 等待显示窗口关闭
            }
        }

        /// <summary>
        /// 获取图像属性
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void GetImageInfo(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var image = ViewModel.MainImage.ToMat(); // 获取图像

                ViewModel.Width = image.Width; // 获取图像宽度
                ViewModel.Height = image.Height; // 获取图像高度
                ViewModel.Channels = image.Channels(); // 获取图像通道数
            }
        }

        /// <summary>
        /// 创建空白图像
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CreateImage(object sender, RoutedEventArgs e)
        {
            // MatType.CV_8UC1 - 1通道的图像 - 常用
            // MatType.CV_8UC2 - 2通道的图像
            // MatType.CV_8UC3 - 3通道的图像 - 常用
            // MatType.CV_8UC4 - 4通道的图像

            // 创建空白图像1
            // Size size:设置图像大小
            // MatType type: 设置图像通道数
            // Scalar s: 设置图像颜色  颜色顺序是BGR(蓝、绿、红)，数值大的颜色显示明显
            Mat blankMat = new Mat(new OpenCvSharp.Size(200, 300), MatType.CV_8UC3, new Scalar(20, 23, 120));  // 创建3通道蓝色空白图像
            Cv2.ImShow("显示3通道空白图像", blankMat);

            // 创建空白图像2
            // int rows:2D 数组中的行数（即 - 图像的高度（以像素为单位）
            // int cols:2D 数组中的列数（即 - 图像的宽度（以像素为单位）
            // MatType type: 设置图像通道数
            // Scalar s: 设置图像颜色  颜色顺序是BGR(蓝、绿、红)，数值大的颜色显示明显
            Mat blankMat2 = new Mat(600, 800, MatType.CV_8UC3, new Scalar(200, 23, 120));  // 创建3通道蓝色空白图像
            Cv2.ImShow("显示3通道空白图像", blankMat2);

            Mat newImage = Mat.Ones(new OpenCvSharp.Size(100, 100), MatType.CV_8UC3);  // 图像默认黑色
            ViewModel.MainImage = newImage.ToBitmapSource(); // 显示图像

            Cv2.WaitKey(0); // 让图像弹窗等待关闭
        }

        /// <summary>
        /// 图像亮度增强
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ImageBrightEnhance(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取mat图像

                // 对图像进行亮度增强
                Mat imageBright50 = new Mat();
                // OutputArray m, 图像
                // MatType rtype, 输出图像的类型。 如果rtype为负，则输出矩阵将具有与输入的类型相同。
                // double alpha = 1,  在分配给输出图像之前，输入图像中的每个像素将乘以此数字。
                // double beta = 0 此值将添加到输入图像中的每个像素并分配给输出图像。
                mat.ConvertTo(imageBright50, -1, 1, 50); // 对图像亮度增强50
                ViewModel.BinarizationImage = imageBright50.ToBitmapSource();

                Mat imageBright100 = new Mat();
                mat.ConvertTo(imageBright50, -1, 1, 100); // 对图像亮度增强50
                ViewModel.GrayImage = imageBright50.ToBitmapSource();
            }
        }

        /// <summary>
        /// 图像亮度降低
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ImageBrightDecrease(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取mat图像

                // 对图像进行亮度降低
                Mat imageBright50 = new Mat();
                // OutputArray m, 图像
                // MatType rtype, 输出图像的类型。 如果rtype为负，则输出矩阵将具有与输入的类型相同。
                // double alpha = 1,  在分配给输出图像之前，输入图像中的每个像素将乘以此数字。
                // double beta = 0 此值将添加到输入图像中的每个像素并分配给输出图像。
                mat.ConvertTo(imageBright50, -1, 1, -50); // 对图像亮度增强50
                ViewModel.BinarizationImage = imageBright50.ToBitmapSource();

                Mat imageBright100 = new Mat();
                mat.ConvertTo(imageBright50, -1, 1, -100); // 对图像亮度增强50
                ViewModel.GrayImage = imageBright50.ToBitmapSource();
            }
        }

        /// <summary>
        /// 视频亮度增强
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void VideoBrightEnhance(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径
                using var capture = new VideoCapture(imgPath);  // 创建VideoCapture对象并打开视频文件
                // 检查视频是否成功打开
                if (capture.IsOpened())
                {
                    while (true) // 循环读取帧
                    {
                        // 读取新的帧
                        Mat frame = new Mat();
                        bool isSuccess = capture.Read(frame); // 读取新的帧

                        ViewModel.MainImage = frame.ToBitmapSource(); // 显示原图像

                        // 对视频进行亮度增强
                        Mat imageBright50 = new Mat();
                        // OutputArray m, 图像
                        // MatType rtype, 输出图像的类型。 如果rtype为负，则输出矩阵将具有与输入的类型相同。
                        // double alpha = 1,  在分配给输出图像之前，输入图像中的每个像素将乘以此数字。
                        // double beta = 0 此值将添加到输入图像中的每个像素并分配给输出图像。
                        frame.ConvertTo(imageBright50, -1, 1, 50); // 对图像亮度增强50
                        ViewModel.BinarizationImage = imageBright50.ToBitmapSource();

                        Mat imageBright100 = new Mat();
                        frame.ConvertTo(imageBright50, -1, 1, 100); // 对图像亮度增强50
                        ViewModel.GrayImage = imageBright50.ToBitmapSource();

                        // 等待键盘输入，如果按下'ESC'键退出循环
                        int key = Cv2.WaitKey(20); // 接收到键盘按键后等待20毫秒返回按键值
                        if (key == 27) // 'ESC' key
                        {
                            Console.WriteLine("按下了ESC建，停止播放视频!");
                            break;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 视频亮度降低
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void VideoBrightDecrease(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径
                using var capture = new VideoCapture(imgPath);  // 创建VideoCapture对象并打开视频文件
                // 检查视频是否成功打开
                if (capture.IsOpened())
                {
                    while (true) // 循环读取帧
                    {
                        // 读取新的帧
                        Mat frame = new Mat();
                        bool isSuccess = capture.Read(frame); // 读取新的帧

                        ViewModel.MainImage = frame.ToBitmapSource(); // 显示原图像

                        // 对视频进行亮度降低
                        Mat imageBright50 = new Mat();
                        // OutputArray m, 图像
                        // MatType rtype, 输出图像的类型。 如果rtype为负，则输出矩阵将具有与输入的类型相同。
                        // double alpha = 1,  在分配给输出图像之前，输入图像中的每个像素将乘以此数字。
                        // double beta = 0 此值将添加到输入图像中的每个像素并分配给输出图像。
                        frame.ConvertTo(imageBright50, -1, 1, -50); // 对图像亮度减少50
                        ViewModel.BinarizationImage = imageBright50.ToBitmapSource();

                        Mat imageBright100 = new Mat();
                        frame.ConvertTo(imageBright50, -1, 1, -100); // 对图像亮度减少100
                        ViewModel.GrayImage = imageBright50.ToBitmapSource();

                        // 等待键盘输入，如果按下'ESC'键退出循环
                        int key = Cv2.WaitKey(20); // 接收到键盘按键后等待20毫秒返回按键值
                        if (key == 27) // 'ESC' key
                        {
                            Console.WriteLine("按下了ESC建，停止播放视频!");
                            break;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 图像对比度增强
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ImageContrastEnhance(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取mat图像

                // 对图像进行亮度增强
                Mat imageBright50 = new Mat();
                // OutputArray m, 图像
                // MatType rtype, 输出图像的类型。 如果rtype为负，则输出矩阵将具有与输入的类型相同。
                // double alpha = 1,  在分配给输出图像之前，输入图像中的每个像素将乘以此数字。
                // double beta = 0 此值将添加到输入图像中的每个像素并分配给输出图像。
                mat.ConvertTo(imageBright50, -1, 2, 0); // 图像对比度增强两倍
                ViewModel.BinarizationImage = imageBright50.ToBitmapSource();

                Mat imageBright100 = new Mat();
                mat.ConvertTo(imageBright50, -1, 4, 0); // 图像对比度增强4倍
                ViewModel.GrayImage = imageBright50.ToBitmapSource();
            }
        }

        /// <summary>
        /// 图像对比度降低
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ImageContrastReduced(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取mat图像

                // 对图像进行亮度降低
                Mat imageBright50 = new Mat();
                // OutputArray m, 图像
                // MatType rtype, 输出图像的类型。 如果rtype为负，则输出矩阵将具有与输入的类型相同。
                // double alpha = 1,  在分配给输出图像之前，输入图像中的每个像素将乘以此数字。
                // double beta = 0 此值将添加到输入图像中的每个像素并分配给输出图像。
                mat.ConvertTo(imageBright50, -1, 0.5, 0); // 图像对比度降低0.5倍
                ViewModel.BinarizationImage = imageBright50.ToBitmapSource();

                Mat imageBright100 = new Mat();
                mat.ConvertTo(imageBright50, -1, 0.25, 0); // 图像对比度降低0.25倍
                ViewModel.GrayImage = imageBright50.ToBitmapSource();
            }
        }

        /// <summary>
        /// 视频对比度增强
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void VideoContrastEnhance(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径
                using var capture = new VideoCapture(imgPath);  // 创建VideoCapture对象并打开视频文件
                // 检查视频是否成功打开
                if (capture.IsOpened())
                {
                    while (true) // 循环读取帧
                    {
                        // 读取新的帧
                        Mat frame = new Mat();
                        bool isSuccess = capture.Read(frame); // 读取新的帧

                        ViewModel.MainImage = frame.ToBitmapSource(); // 显示原图像

                        // 对视频进行亮度增强
                        Mat imageBright50 = new Mat();
                        // OutputArray m, 图像
                        // MatType rtype, 输出图像的类型。 如果rtype为负，则输出矩阵将具有与输入的类型相同。
                        // double alpha = 1,  在分配给输出图像之前，输入图像中的每个像素将乘以此数字。
                        // double beta = 0 此值将添加到输入图像中的每个像素并分配给输出图像。
                        frame.ConvertTo(imageBright50, -1, 2, 0); // 视频对比度增强2倍
                        ViewModel.BinarizationImage = imageBright50.ToBitmapSource();

                        Mat imageBright100 = new Mat();
                        frame.ConvertTo(imageBright50, -1, 4, 0); // 视频对比度增强4倍
                        ViewModel.GrayImage = imageBright50.ToBitmapSource();
                    }
                }
            }
        }

        /// <summary>
        /// 视频对比度降低
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void VideoContrastReduced(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径
                using var capture = new VideoCapture(imgPath);  // 创建VideoCapture对象并打开视频文件
                // 检查视频是否成功打开
                if (capture.IsOpened())
                {
                    while (true) // 循环读取帧
                    {
                        // 读取新的帧
                        Mat frame = new Mat();
                        bool isSuccess = capture.Read(frame); // 读取新的帧

                        ViewModel.MainImage = frame.ToBitmapSource(); // 显示原图像

                        // 对视频进行亮度降低
                        Mat imageBright50 = new Mat();
                        // OutputArray m, 图像
                        // MatType rtype, 输出图像的类型。 如果rtype为负，则输出矩阵将具有与输入的类型相同。
                        // double alpha = 1,  在分配给输出图像之前，输入图像中的每个像素将乘以此数字。
                        // double beta = 0 此值将添加到输入图像中的每个像素并分配给输出图像。
                        frame.ConvertTo(imageBright50, -1, 0.5, 0); // 视频对比度降低0.5倍
                        ViewModel.BinarizationImage = imageBright50.ToBitmapSource();

                        Mat imageBright100 = new Mat();
                        frame.ConvertTo(imageBright50, -1, 0.25, 0); // 视频对比度降低0.25倍
                        ViewModel.GrayImage = imageBright50.ToBitmapSource();
                    }
                }
            }
        }

        /// <summary>
        /// 灰度直方图均衡
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void GrayHistogramEqualization(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径

                // 加载灰度图像
                Mat src = Cv2.ImRead(imgPath, ImreadModes.Grayscale);

                // 创建一个用于存储结果的Mat对象
                Mat dst = new Mat();

                // 对灰度图像进行直方图均衡
                Cv2.EqualizeHist(src, dst);

                // 显示结果
                Cv2.ImShow("灰度图", src);
                Cv2.ImShow("均衡图", dst);

                // 等待任意键按下
                Cv2.WaitKey(0);

                // 销毁所有窗口
                Cv2.DestroyAllWindows();

                // 释放资源
                src.Dispose();
                dst.Dispose();
            }
        }

        /// <summary>
        /// 彩色图像直方图均衡1
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ColorHistogramEqualization1(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径

                Mat colorImage = Cv2.ImRead(imgPath);  // 加载彩色图像

                // 将图像转换为灰度单通道图像
                Mat[] channels = Cv2.Split(colorImage);

                // 对每个通道进行直方图均衡化
                Mat[] equalizedChannels = new Mat[channels.Length];
                for (int i = 0; i < channels.Length; i++)
                {
                    Mat mat = new Mat();
                    // 对灰度图像进行直方图均衡
                    // InputArray src: 原图
                    // OutputArray dst：均衡后的图像
                    Cv2.EqualizeHist(channels[i], mat);
                    equalizedChannels[i] = mat;
                }

                Mat equalizedImage = new Mat(); // 均衡后的图像
                // 合并均衡化后的通道
                Cv2.Merge(equalizedChannels, equalizedImage);

                // 显示原图像
                Cv2.ImShow("原图像图像", colorImage);
                // 显示均衡图像
                Cv2.ImShow("均衡图像", equalizedImage);
                Cv2.WaitKey(0);

                // 保存图像
                //Cv2.ImWrite("equalized_image.jpg", equalizedImage);

                // 释放资源
                colorImage.Dispose();
                foreach (Mat channel in channels)
                {
                    channel.Dispose();
                }
                foreach (Mat eqChannel in equalizedChannels)
                {
                    eqChannel.Dispose();
                }
                equalizedImage.Dispose();
            }
        }

        /// <summary>
        /// 彩色图像直方图均衡2
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ColorHistogramEqualization2(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径

                Mat colorImage = Cv2.ImRead(imgPath);  // 加载彩色图像

                if (colorImage.Empty())
                {
                    MessageBox.Show("加载图像失败");
                    return;
                }

                Mat histEqualizedImage = new Mat(); // 直方图均衡后的图像
                // 直方图均衡只能处理强度信息，不能处理带颜色的通道
                Cv2.CvtColor(colorImage, histEqualizedImage, ColorConversionCodes.BGR2YCrCb); // 把图像从BGR色彩空间转换为YCrCb色彩空间

                //把转换好的色彩空间对象分割处对应通道分别是Y(仅包含强度信息)，Cr(红色)，Cb(蓝色)并把结果存储到vector集合中
                Mat[] vec_channels = new Mat[3];
                Cv2.Split(histEqualizedImage, out vec_channels);

                //把拆分出来的Y通道进行直方图均衡
                Cv2.EqualizeHist(vec_channels[0], vec_channels[0]);

                //合并处理好的3通道数据合并到YCrCb的色彩空间中
                Cv2.Merge(vec_channels, histEqualizedImage);

                //把YCrCb的色彩空间图像转换到BGR颜色空间
                Cv2.CvtColor(histEqualizedImage, histEqualizedImage, ColorConversionCodes.YCrCb2BGR);

                // 显示原图像
                Cv2.ImShow("原图像图像", colorImage);
                // 显示均衡图像
                Cv2.ImShow("均衡图像", histEqualizedImage);
                Cv2.WaitKey(0);
            }
        }

        /// <summary>
        /// 视频直方图均衡
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnVideoHistogramEqualization1(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径

                // 创建打开视频文件对象
                VideoCapture capture = new VideoCapture(imgPath);

                Mat frame = new Mat(); // 读取视频帧
                Mat equalizedFrame = new Mat(); // 均衡后的图像

                while (true)
                {
                    // 读取视频帧
                    if (!capture.Read(frame))
                        break;

                    // 将图像转换为灰度单通道图像集合
                    Mat[] channels = Cv2.Split(frame);

                    // 对每个通道进行直方图均衡化
                    Mat[] equalizedChannels = new Mat[channels.Length];
                    for (int i = 0; i < channels.Length; i++)
                    {
                        Mat mat = new Mat();
                        // 对灰度图像进行直方图均衡
                        // InputArray src: 原图
                        // OutputArray dst：均衡后的图像
                        Cv2.EqualizeHist(channels[i], mat);
                        equalizedChannels[i] = mat;
                    }

                    Cv2.Merge(equalizedChannels, equalizedFrame); // 合并均衡化后的通道

                    //ViewModel.MainImage = frame.ToBitmapSource(); // 显示原图像
                    //ViewModel.BinarizationImage = equalizedFrame.ToBitmapSource();

                    Cv2.ImShow("原图像", frame); // 显示原图像
                    // 显示均衡化后的图像
                    Cv2.ImShow("直方图均衡图像", equalizedFrame);
                    if (Cv2.WaitKey(25) >= 0)
                        break;
                }

                // 释放资源
                capture.Dispose();
                frame.Dispose();
                equalizedFrame.Dispose();

                // 关闭所有窗口
                Cv2.DestroyAllWindows();
            }
        }

        /// <summary>
        /// 视频直方图均衡2
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnVideoHistogramEqualization2(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径

                // 创建打开视频文件对象
                VideoCapture capture = new VideoCapture(imgPath);
                if (!capture.IsOpened())
                {
                    MessageBox.Show("打开视频失败!");
                    return;
                }

                Mat frame = new Mat(); // 读取视频帧
                Mat equalizedFrame = new Mat(); // 均衡后的图像

                while (true) // 开始循环读取视频的每一帧
                {
                    // 读取视频帧
                    if (!capture.Read(frame)) // 判断视频是否已经读取完成
                        break;

                    // 直方图均衡只能处理强度信息，不能处理带颜色的通道
                    Cv2.CvtColor(frame, equalizedFrame, ColorConversionCodes.BGR2YCrCb); // 把图像从BGR色彩空间转换为YCrCb色彩空间

                    //把转换好的色彩空间对象分割处对应通道分别是Y(仅包含强度信息)，Cr(红色)，Cb(蓝色)并把结果存储到vector集合中
                    Mat[] vec_channels = new Mat[3];
                    Cv2.Split(equalizedFrame, out vec_channels);

                    //把拆分出来的Y通道进行直方图均衡
                    Cv2.EqualizeHist(vec_channels[0], vec_channels[0]);

                    //合并处理好的3通道数据合并到YCrCb的色彩空间中
                    Cv2.Merge(vec_channels, equalizedFrame);

                    //把YCrCb的色彩空间图像转换到BGR颜色空间
                    Cv2.CvtColor(equalizedFrame, equalizedFrame, ColorConversionCodes.YCrCb2BGR);

                    // 显示原图像
                    Cv2.ImShow("原图像图像", frame);
                    // 显示均衡图像
                    Cv2.ImShow("均衡图像", equalizedFrame);
                    if (Cv2.WaitKey(25) >= 0)
                        break;
                }

                // 释放资源
                capture.Dispose();
                frame.Dispose();
                equalizedFrame.Dispose();

                // 关闭所有窗口
                Cv2.DestroyAllWindows();
            }
        }

        /// <summary>
        /// 图像上的均匀模糊
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnImageUniformBlurring(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径
                                            // 加载图像
                Mat src = Cv2.ImRead(imgPath);

                // 指定模糊核的大小和标准差
                OpenCvSharp.Size ksize = new OpenCvSharp.Size(5, 5); // 核大小

                // 应用均匀模糊
                Mat dst = src.Blur(ksize);

                // 显示原图
                Cv2.ImShow("原图", src);
                // 显示结果
                Cv2.ImShow("图像均匀模糊", dst);
                Cv2.WaitKey(0);

                // 释放资源
                src.Dispose();
                dst.Dispose();
            }
        }

        /// <summary>
        /// 图像上的均匀模糊2
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnImageUniformBlurring2(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径
                Mat image = Cv2.ImRead(imgPath); // 读取图像
                // 创建3X3和5X5的均匀滤波核并对读取图像进行均匀滤波处理
                Mat blur_3X3_img = image.Blur(new OpenCvSharp.Size(3, 3));
                Mat blur_5X5_img = image.Blur(new OpenCvSharp.Size(5, 5));

                // 显示出来图像到窗口
                Cv2.ImShow("原始图像", image);
                Cv2.ImShow("3X3处理的图像", blur_3X3_img);
                Cv2.ImShow("5X5处理的图像", blur_5X5_img);

                Cv2.WaitKey(0); // 等待

                Cv2.DestroyAllWindows(); // 释放窗体资源
            }
        }

        /// <summary>
        /// 视频均匀模糊1
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnVideoUniformBlurring1(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径

                // 打开视频捕捉设备或者视频文件
                VideoCapture capture = new VideoCapture(imgPath);
                if (!capture.IsOpened())
                {
                    MessageBox.Show("打开视频失败!");
                    return;
                }

                Mat frame = new Mat(); // 读取到的视频帧
                Mat blurredFrame = new Mat(); // 模糊处理后的视频帧

                while (true) // 循环读取视频帧
                {
                    if (!capture.Read(frame)) // 读取视频帧
                        break; // 如果读取到视频的末尾，则退出循环

                    // 应用模糊处理
                    Cv2.Blur(frame, blurredFrame, new OpenCvSharp.Size(15, 15));
                    //blurredFrame = frame.Blur(new OpenCvSharp.Size(5, 5)); // 两种不同的写法效果是一样的

                    // 显示原始视频
                    ViewModel.MainImage = frame.ToBitmapSource(); // 显示视频帧

                    // 显示模糊视频
                    ViewModel.BinarizationImage = blurredFrame.ToBitmapSource(); // 显示视频帧
                    //// 显示原始视频
                    //Cv2.ImShow("显示原视频", frame);
                    //// 显示帧（可选）
                    //Cv2.ImShow("显示模糊视频", blurredFrame);

                    if (Cv2.WaitKey(25) >= 0)
                        break;
                }

                // 释放资源
                capture.Release();
                Cv2.DestroyAllWindows(); // 释放窗体资源
            }
        }

        /// <summary>
        /// 图像高斯模糊处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnImageGaussianBlur(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径

                Mat src = Cv2.ImRead(imgPath); // 读取图像

                // 高斯模糊的核大小，这里是3x3
                OpenCvSharp.Size ksize3_3 = new OpenCvSharp.Size(3, 3);
                // 高斯模糊的核大小，这里是5x5
                OpenCvSharp.Size ksize5_5 = new OpenCvSharp.Size(5, 5);

                // 高斯模糊的标准差，如果为0，则根据核大小自动计算
                double sigmaX = 0;

                // 图像高斯模糊
                Mat dst3_3 = src.GaussianBlur(ksize3_3, sigmaX);

                // 图像高斯模糊
                Mat dst5_5 = src.GaussianBlur(ksize5_5, sigmaX);

                // 显示结果
                Cv2.ImShow("显示原图", src);
                Cv2.ImShow("显示3*3高斯模糊图", dst3_3);
                Cv2.ImShow("显示5*5高斯模糊图", dst5_5);

                // 等待按键事件
                Cv2.WaitKey(0);

                // 释放资源
                src.Dispose();
                dst3_3.Dispose();
                dst5_5.Dispose();

                // 关闭所有窗口
                Cv2.DestroyAllWindows();
            }
        }

        /// <summary>
        /// 视频高斯模糊处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnVidelGaussianBlur(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径

                // 打开视频捕捉设备或者视频文件
                VideoCapture capture = new VideoCapture(imgPath);
                if (!capture.IsOpened())
                {
                    MessageBox.Show("打开视频失败!");
                    return;
                }

                Mat frame = new Mat(); // 读取到的视频帧
                Mat blurredFrame = new Mat(); // 模糊处理后的视频帧

                while (true) // 循环读取视频帧
                {
                    if (!capture.Read(frame)) // 读取视频帧
                        break; // 如果读取到视频的末尾，则退出循环

                    double sigmaX = 0; // 高斯模糊的标准差，如果为0，则根据核大小自动计算

                    // 应用模糊处理
                    //Cv2.GaussianBlur(frame, blurredFrame, new OpenCvSharp.Size(15, 15), sigmaX);
                    blurredFrame = frame.GaussianBlur(new OpenCvSharp.Size(5, 5), sigmaX); // 两种不同的写法效果是一样的

                    // 显示原始视频
                    ViewModel.Title1 = "原始视频";
                    ViewModel.MainImage = frame.ToBitmapSource(); // 显示视频帧

                    // 显示模糊视频
                    ViewModel.Title2 = "模糊视频";
                    ViewModel.BinarizationImage = blurredFrame.ToBitmapSource(); // 显示视频帧
                    //// 显示原始视频
                    //Cv2.ImShow("显示原视频", frame);
                    //// 显示帧（可选）
                    //Cv2.ImShow("显示模糊视频", blurredFrame);

                    if (Cv2.WaitKey(25) >= 0)
                        break;
                }

                // 释放资源
                capture.Release();
                Cv2.DestroyAllWindows(); // 释放窗体资源
            }
        }

        /// <summary>
        /// 图像反转
        /// 算法：反转后的像素a=255-a;
        /// 在视觉当中处理图像的时候，有一种反转求灰度图的在反转图，这样的反转图像在转轮廓的话会非常清晰，这是一种套路。
        /// 在轮廓区域区分不明确的时候可以用这种方法做，这样可以让轮廓区域比较明晰。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnImageReverse1(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径
                                            // 加载图像
                Mat src = Cv2.ImRead(imgPath);

                // 创建一个Mat对象用于存储反转后的图像
                Mat dst = new Mat();

                // 彩色图像翻转操作
                Cv2.BitwiseNot(src, dst);

                //// 保存结果
                //Cv2.ImWrite("inverted_image.jpg", dst);

                // 显示图像（可选）
                Cv2.ImShow("原彩色图像", src);

                // 显示图像（可选）
                Cv2.ImShow("反转彩色图像", dst);

                //灰度化，由反转彩色图像获得灰度图像
                var gray = dst.CvtColor(ColorConversionCodes.BGR2GRAY);
                // 显示图像（可选）
                Cv2.ImShow("原灰度图像", gray);

                Mat grayNot = new Mat();
                // 灰度图像翻转操作
                Cv2.BitwiseNot(gray, grayNot);
                // 显示图像（可选）
                Cv2.ImShow("反转灰度图像", grayNot);

                Cv2.WaitKey(0);

                // 释放资源
                src.Dispose();
                dst.Dispose();
            }
        }

        /// <summary>
        /// 图像反转2
        /// 算法：反转后的像素a=255-a;
        /// 这个代码有bug，没运行起来。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnImageReverse2(object sender, RoutedEventArgs e)
        {
            try
            {
                OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
                if (ofd.ShowDialog() == true)
                {
                    var imgPath = ofd.FileName; // 获取文件路径

                    Mat color;  //定义原图像
                                //定义灰度图像，灰度图像反转后的图像
                    Mat gray = new Mat();
                    Mat grayDst = new Mat();
                    // 定义图像的高度和宽度
                    int height, width;

                    //载入图片
                    color = Cv2.ImRead(imgPath);
                    if (color.Empty())
                    {
                        MessageBox.Show("读取错误");
                        return;
                    }

                    //获取图像信息
                    height = color.Rows; // 获取图像的高度

                    //列项要乘通道数
                    width = color.Cols * color.Channels(); // 彩色图像有多个通道
                    Debug.WriteLine($"彩色图片宽:{color.Cols}, 高:{color.Rows}，通道数:{color.Channels()},实际宽:{width}");

                    //显示原图
                    Cv2.ImShow("原彩色原图", color);

                    var colorDst = color.Clone(); // 克隆图像
                                                  //彩色图像反转
                                                  //算法：反转后的像素a = 255 - a
                    for (int i = 0; i < height; i++)
                    {
                        for (int j = 0; j < width; j++)
                        {
                            // 目前这里报错需要解决。
                            // 指定的数组元素返回一个像素值
                            colorDst.At<uint>(i, j) = 255 - colorDst.At<uint>(i, j);   //对每一个像素反转
                        }
                    }

                    //显示反相图片
                    Cv2.ImShow("反转彩色图像", colorDst);

                    //---------------------灰度图像反转------------------
                    //灰度化，由原图获得灰度图像
                    Cv2.CvtColor(color, gray, ColorConversionCodes.BGR2GRAY);

                    // 获取灰度图的高
                    height = gray.Rows;
                    //列项乘通道数
                    // 获取灰度图的宽度
                    width = gray.Cols * gray.Channels();

                    Debug.WriteLine($"灰度图片宽:{color.Cols}, 高:{color.Rows}，通道数:{color.Channels()},实际宽:{width}");

                    //显示灰度图像
                    Cv2.ImShow("原灰度窗口", gray);

                    //灰度图像反转
                    //算法：反转后的像素a = 255 - a
                    grayDst = gray.Clone(); // 克隆图像
                    for (int i = 0; i < gray.Rows; i++)
                    {
                        for (int j = 0; j < gray.Cols; j++)
                        {
                            //灰度反转
                            grayDst.At<uint>(i, j) = 255 - gray.At<uint>(i, j);
                        }
                    }
                    //显示反转图像
                    Cv2.ImShow("反转灰度窗口", grayDst);

                    //暂停，保持图像显示
                    Cv2.WaitKey(0);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
                MessageBox.Show(ex.Message);
            }
        }

        /// <summary>
        /// 五种滤波方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnFiveFilterMethod(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径

                Mat image = Cv2.ImRead(imgPath); // 读取图像
                Cv2.ImShow("显示原图", image);  // 显示原图

                #region 高斯模糊
                // 高斯模糊的核大小，这里是5x5
                OpenCvSharp.Size ksize5_5 = new OpenCvSharp.Size(5, 5);
                // 高斯模糊的标准差，如果为0，则根据核大小自动计算
                double sigmaX = 0;
                /*GaussianBlur函数是OpenCVSharp中用于进行高斯滤波的方法之一。高斯滤波是一种常用的图像平滑处理技术，它能够有效地去除图像中的高斯噪声，同时保留图像的边缘信息。
                 * 该方法基于高斯函数对图像进行卷积操作，通过对每个像素周围的邻域进行加权平均来实现平滑处理。*/
                // 图像高斯模糊
                // src：输入图像，即待处理的原始图像。
                // dst：输出图像，即处理后的图像。
                // ksize：滤波核大小，指定高斯核的宽度和高度，一般为奇数。
                // sigmaX：高斯核在X方向上的标准差，如果为0则根据核大小自动计算。
                Mat dst5_5 = image.GaussianBlur(ksize5_5, sigmaX);
                // 显示高斯图像
                Cv2.ImShow("显示5*5高斯模糊图", dst5_5);
                #endregion

                #region 中值滤波
                Mat medianFiltered = new Mat();
                /*中值滤波是一种非线性滤波方法，它通过对图像像素值进行排序，并选择中间值来代替原始像素值，从而实现图像的平滑处理。
                 * 中值滤波在处理椒盐噪声等脉冲噪声时效果非常好，可以有效地保留图像的细节信息。*/
                // 中值滤波
                // src：输入图像，即待处理的原始图像。
                // dst：输出图像，即处理后的图像。
                // ksize：滤波核的大小，指定滤波窗口的边长，一般为奇数。
                Cv2.MedianBlur(image, medianFiltered, 5);
                Cv2.ImShow("中值滤波图像", medianFiltered);
                #endregion

                #region 均值滤波
                Mat meanFiltered = new Mat();
                /*均值滤波是一种简单的线性滤波方法，它通过对图像像素进行简单的平均来实现图像的平滑处理。均值滤波在一定程度上可以去除图像中的噪声，
                 * 但对于较大的噪声或者需要保留边缘细节的图像处理任务效果可能不够理想。*/
                // 参数说明：
                // src：输入图像，即待处理的原始图像。
                // dst：输出图像，即处理后的图像。
                // ksize：滤波核的大小，指定滤波窗口的宽度和高度，一般为奇数。
                Cv2.Blur(image, meanFiltered, new OpenCvSharp.Size(5, 5));
                Cv2.ImShow("均值滤波图像", meanFiltered);
                #endregion

                #region 双边滤波
                /*双边滤波是一种非线性滤波方法，它能够在保留图像边缘的同时对图像进行平滑处理。与其他滤波方法不同，双边滤波不仅考虑像素之间的空间距离，
                 * 还考虑像素之间的灰度差异，因此能够更好地保留图像的细节。*/
                Mat bilateralFiltered = new Mat();
                // 参数说明：
                // src：输入图像，即待处理的原始图像。
                // dst：输出图像，即处理后的图像。
                // d：滤波操作的直径，表示在像素相似性计算中使用的邻域直径。
                // sigmaColor：颜色空间的标准差，控制颜色相似性权重。
                // sigmaSpace：坐标空间的标准差，控制空间相似性权重。
                Cv2.BilateralFilter(image, bilateralFiltered, 9, 75, 75);
                Cv2.ImShow("双边滤波图像", bilateralFiltered);
                #endregion

                #region 方框滤波
                /*方框滤波是一种线性滤波方法，它使用一个固定大小的滤波器对图像进行卷积操作，从而实现图像的平滑处理。该滤波器中的每个元素都具有相同的权重，
                 * 因此可以看作是对图像进行简单的平均处理。*/
                Mat boxFiltered = new Mat();
                // 参数说明：
                // src：输入图像，即待处理的原始图像。
                // dst：输出图像，即处理后的图像。
                // ddepth：输出图像的深度，-1表示与输入图像相同,或image.Depth()获取原图深度。
                // ksize：滤波核的大小，指定滤波窗口的宽度和高度，一般为奇数。
                Cv2.BoxFilter(image, boxFiltered, -1, new OpenCvSharp.Size(5, 5));
                //Cv2.BoxFilter(image, boxFiltered, image.Depth(), new OpenCvSharp.Size(5, 5));
                Cv2.ImShow("方框滤波图像", boxFiltered);
                #endregion

                #region 自定义滤波
                /*自定义滤波函数允许用户根据需求自定义滤波器的卷积核，从而实现各种滤波效果，如锐化、边缘检测等。
                 * 通过自定义滤波器，用户可以灵活地对图像进行滤波处理，满足不同场景下的需求。*/
                Mat kernel = new Mat(3, 3, MatType.CV_32F, new float[] { -1, -1, -1, -1, 9, -1, -1, -1, -1 }); // 滤波器的卷积核
                Mat filteredImage = new Mat();
                // 参数说明：
                // src：输入图像，即待处理的原始图像。
                // dst：输出图像，即处理后的图像。
                // ddepth：输出图像的深度，-1表示与输入图像相同。
                // kernel：滤波核，即卷积核，用于对图像进行滤波处理。
                Cv2.Filter2D(image, filteredImage, -1, kernel);
                Cv2.ImShow("自定义滤波图像", filteredImage);
                #endregion

                // 等待按键事件
                Cv2.WaitKey(0);

                // 关闭所有窗口
                Cv2.DestroyAllWindows();
            }
        }

        /// <summary>
        /// 形态学操作
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnMorphologicalOperation(object sender, RoutedEventArgs e)
        {
            int[][] mat = {
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 0],
            [0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0],
            [0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0],
            [0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0],
            [0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0],
            [0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 0],
            [0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            };
            Mat image = new Mat();
            OpenCvSharp.Size size = new OpenCvSharp.Size(9, 12);

            //// 在矩形区域内为每个像素赋值
            //for (int y = 0; y < rectSize.Height; y++)
            //{
            //    for (int x = 0; x < rectSize.Width; x++)
            //    {
            //        // 使用索引器为(x, y)位置的像素赋值
            //        image.At<byte>(rectStart.Y + y, rectStart.X + x) = 255;
            //    }
            //}

            Mat src = new Mat(9, 12, MatType.CV_8UC1, new byte[] {
                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 0,
                0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
                0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
                0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0,
                0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
                0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 0,
                0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 });
            Cv2.ImShow("验证形态学应用的二值化矩阵", src);
            // 0是黑色 255是白色
            // 创建3X3矩形核元素
            Mat kernel = Cv2.GetStructuringElement(0, new OpenCvSharp.Size(3, 3));
            // 对象矩阵进行膨胀操作
            Mat dilateImg = new Mat();
            #region 膨胀操作
            // 说明:根据核的大小对255白色区域进行膨胀
            // 膨胀操作
            // 参数说明
            // src：输入图像，图像的通道数可以是任意的，但是图像的数据类型必须是CV_8U，CV_16U，CV_16S，CV_32F或CV_64F之一。
            // dst：形态学操作后的输出图像，与输入图像具有相同的尺寸和数据类型。
            // op：形态学操作类型的标志，可以选择的标志及含义在表6 - 6中给出。
            // kernel：结构元素，可以自己生成，也可以用getStructuringElement()函数生成。
            Cv2.MorphologyEx(src, dilateImg, MorphTypes.Dilate, kernel);
            Cv2.ImShow("图像膨胀操作", dilateImg);
            #endregion

            #region 腐蚀图像
            // 说明：根据核的大小对255白色区域缩小，去掉杂质小颗粒
            Mat corruptImg = new Mat(); // 腐蚀图像
            Cv2.MorphologyEx(src, corruptImg, MorphTypes.Erode, kernel);
            Cv2.ImShow("图像膨胀操作", corruptImg);
            #endregion

            #region 对象矩阵进行开操作
            // 说明：开：腐蚀再膨胀，去除微小干扰块。
            // 作用：去掉杂质小颗粒后再还原图像。
            Mat openImg = new Mat(); // 开运算图像
            Cv2.MorphologyEx(src, openImg, MorphTypes.Open, kernel);
            Cv2.ImShow("开运算图像", openImg);
            #endregion

            #region 对象矩阵进行闭操作
            // 说明：闭：膨胀再腐蚀，填充闭合区域。
            // 作用：膨胀后把孔洞填充，然后在还原图像，主要作用是填充图像中的孔洞的。
            Mat closeImg = new Mat(); // 闭运算图像
            Cv2.MorphologyEx(src, closeImg, MorphTypes.Close, kernel);
            Cv2.ImShow("闭运算图像", closeImg);
            #endregion

            #region 对象矩阵进顶帽操作
            // 说明：顶帽：原图 - 开操作后的图
            // 作用：去掉完成图像，只保留杂质小颗粒。
            Mat topHatImg = new Mat(); // 闭运算图像
            Cv2.MorphologyEx(src, topHatImg, MorphTypes.TopHat, kernel);
            Cv2.ImShow("顶帽运算图像", topHatImg);
            #endregion

            #region 对象矩阵进黑帽操作
            // 说明：黑帽：闭操作后的图 - 原图
            // 作用：去掉原图像，保留填充的孔洞区域。
            Mat blackHatImg = new Mat(); // 闭运算图像
            Cv2.MorphologyEx(src, blackHatImg, MorphTypes.BlackHat, kernel);
            Cv2.ImShow("黑帽运算图像", blackHatImg);
            #endregion

            #region 对象矩阵进梯度操作
            // 内梯度：原图膨胀图 - 原图的腐蚀图
            // 外梯度：原膨胀图 - 原图
            // 作用：
            Mat gradientImg = new Mat(); // 闭运算图像
            Cv2.MorphologyEx(src, gradientImg, MorphTypes.Gradient, kernel);
            Cv2.ImShow("梯度运算图像", gradientImg);
            #endregion

            Cv2.WaitKey(0);
        }

        /// <summary>
        /// OpenCv坐标系
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCoordinateSystem(object sender, RoutedEventArgs e)
        {
            // 创建一个3X4矩阵
            Mat matRect = new Mat(3, 4, MatType.CV_8UC1);
            // 打印矩阵
            Debug.WriteLine(matRect);
            // 打印矩阵的行列值
            Debug.WriteLine($"rows :{matRect.Rows}");
            Debug.WriteLine($"cols :{matRect.Cols}");
            // 访问数据
            // matRect.at<uchar>(y,x) => y(row), x(col)
            matRect.At<uint>(0, 2) = 2; // 第1行第3列
            matRect.At<uint>(2, 0) = 4; // 第3行第1列
            // 设置像素点后结果
            Debug.WriteLine(matRect);
        }

        /// <summary>
        /// 三点法仿射变换
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAffineTransforma(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径

                Mat srcImg = Cv2.ImRead(imgPath); // 读取图像
                Cv2.ImShow("显示原图", srcImg);  // 显示原图

                // 定义原图的三个点
                Point2f[] srcPoints = new Point2f[3];
                // 定义映射后的三个坐标点
                Point2f[] dstPoints = new Point2f[3];

                // 采用三角法定位-然后旋转或平移图像
                srcPoints[0] = new Point2f(0, 0);
                srcPoints[1] = new Point2f(0, srcImg.Rows - 1);
                srcPoints[2] = new Point2f(srcImg.Cols - 1, 0);

                // 映射后的三个点
                dstPoints[0] = new Point2f(0, (float)(srcImg.Rows * 0.2));
                dstPoints[1] = new Point2f((float)(srcImg.Cols * 0.25), (float)(srcImg.Rows * 0.75));
                dstPoints[2] = new Point2f((float)(srcImg.Cols * 0.75), (float)(srcImg.Rows * 0.25));

                // 获取仿射变换矩阵
                // 参数
                // src：原图的三个固定顶点（左上角，左下角，右上角）
                // dst：目标图像的三个固定顶点
                //注意，顶点数组长度超过3个，则会自动以前3个为变换顶点；数组可用Point2f[]
                Mat homat2D = Cv2.GetAffineTransform(srcPoints, dstPoints);

                // 进行图像仿射变换操作
                Mat dstImg = new Mat();
                //参数
                //src： 输入图像
                //dst： 输出图像
                //m： 仿射变换矩阵
                //dsize： 图像大小
                Cv2.WarpAffine(srcImg, dstImg, homat2D, srcImg.Size());

                // 显示图像
                Cv2.ImShow("仿射变换图像", dstImg);

                Cv2.WaitKey(0);
            }

        }

        /// <summary>
        /// 角度放射变换
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAngleAffineTransforma(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径

                Mat srcImg = Cv2.ImRead(imgPath); // 读取图像
                Cv2.ImShow("显示原图", srcImg);  // 显示原图

                //设置仿射变换参数
                Point2f centerPoint = new Point2f(srcImg.Cols / 2, srcImg.Rows / 2);  // 获取中心点-表示源图像旋转中心
                double angle = -50; // 旋转角度，正值表示逆时针旋转，负值表示顺时针旋转
                double scale = 0.7; // 缩放系数

                // 获取角度仿射变换矩阵
                // 参数
                // Point2f center, 表示源图像旋转中心
                // double angle, 旋转角度，正值表示逆时针旋转，负值表示顺时针旋转
                // double scale 缩放系数
                Mat homat2D = Cv2.GetRotationMatrix2D(centerPoint, angle, scale);

                // 进行图像仿射变换操作
                Mat dstImg = new Mat();
                //参数
                //src： 输入图像
                //dst： 输出图像
                //m： 仿射变换矩阵
                //dsize： 图像大小
                Cv2.WarpAffine(srcImg, dstImg, homat2D, srcImg.Size());

                // 显示图像
                Cv2.ImShow("仿射变换图像", dstImg);

                Cv2.WaitKey(0);
            }
        }

        /// <summary>
        /// 翻转仿射镜像案例
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnReverseAffineTransforma(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径

                Mat srcImg = Cv2.ImRead(imgPath); // 读取图像
                Cv2.ImShow("显示原图", srcImg);  // 显示原图

                // 定义原图的三个点
                Point2f[] srcPoints = new Point2f[3];
                // 定义映射后的三个坐标点
                Point2f[] dstPoints = new Point2f[3];

                // 采用三角法定位-然后旋转或平移图像
                srcPoints[0] = new Point2f(0, 0);
                srcPoints[1] = new Point2f(0, srcImg.Rows);
                srcPoints[2] = new Point2f(srcImg.Cols, 0);
                // 映射后的三个点
                dstPoints[0] = new Point2f(srcImg.Cols, 0);
                dstPoints[1] = new Point2f(srcImg.Cols, srcImg.Rows);
                dstPoints[2] = new Point2f(0, 0);

                // 获取仿射变换矩阵
                // 参数
                // src：原图的三个固定顶点（左上角，左下角，右上角）
                // dst：目标图像的三个固定顶点
                //注意，顶点数组长度超过3个，则会自动以前3个为变换顶点；数组可用Point2f[]
                Mat homat2D = Cv2.GetAffineTransform(srcPoints, dstPoints);

                // 进行图像仿射变换操作
                Mat dstImg = new Mat();
                //参数
                //src： 输入图像
                //dst： 输出图像
                //m： 仿射变换矩阵
                //dsize： 图像大小
                Cv2.WarpAffine(srcImg, dstImg, homat2D, srcImg.Size());

                // 显示图像
                Cv2.ImShow("仿射变换图像", dstImg);

                Cv2.WaitKey(0);
            }
        }

        /// <summary>
        /// 翻转仿射变换2
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnReverseAffineTransforma2(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径

                Mat srcImg = Cv2.ImRead(imgPath); // 读取图像
                Cv2.ImShow("显示原图", srcImg);  // 显示原图

                var dstImgXY = srcImg.Flip(FlipMode.XY); // 上下左右翻转
                // 显示图像
                Cv2.ImShow("上下左右翻转图像", dstImgXY);
                var dstImgX = srcImg.Flip(FlipMode.X); // 沿Y轴方向翻转
                // 显示图像
                Cv2.ImShow("沿Y轴方向翻转图像", dstImgX);
                var dstImgY = srcImg.Flip(FlipMode.Y); // 沿X轴方向翻转
                // 显示图像
                Cv2.ImShow("沿X轴方向翻转图像", dstImgY);
                //  flipCode：= 0 图像向下翻转
                // XY:-1 上下左右翻转   X:0 沿Y轴方向翻转   Y:1 沿X轴方向翻转
                //> 0 图像向右翻转
                //< 0 图像同时向下向右翻转

                Cv2.WaitKey(0);
            }
        }

        /// <summary>
        /// 透视变换-图片变形
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnPerspectiveTransforma(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径

                Mat srcImg = Cv2.ImRead(imgPath); // 读取图像
                Cv2.ImShow("显示原图", srcImg);  // 显示原图

                // 定义原图的四个点
                Point2f[] srcPoints = new Point2f[4];
                // 定义映射后的4个坐标点
                Point2f[] dstPoints = new Point2f[4];

                // 采用四角法定位-然后旋转或平移图像
                srcPoints[0] = new Point2f(0, 0);
                srcPoints[1] = new Point2f(0, srcImg.Rows);
                srcPoints[2] = new Point2f(srcImg.Cols, 0);
                srcPoints[3] = new Point2f(srcImg.Cols, srcImg.Rows);
                // 映射后的四个点
                dstPoints[0] = new Point2f((float)(srcImg.Cols * 0.1), (float)(srcImg.Rows * 0.1));
                dstPoints[1] = new Point2f(0, srcImg.Rows);
                dstPoints[2] = new Point2f(srcImg.Cols, 0);
                dstPoints[3] = new Point2f((float)(srcImg.Cols * 0.7), (float)(srcImg.Rows * 0.8));

                // 获取仿射变换矩阵
                // 参数
                // src：原图的四个固定顶点（左上角，左下角，右上角）
                // dst：目标图像的四个固定顶点
                //注意，顶点数组长度超过3个，则会自动以前3个为变换顶点；数组可用Point2f[]
                Mat homat2D = Cv2.GetPerspectiveTransform(srcPoints, dstPoints);

                // 进行图像仿射变换操作
                Mat dstImg = new Mat();
                //参数
                //src： 输入图像
                //dst： 输出图像
                //m： 仿射变换矩阵
                //dsize： 图像大小
                dstImg = srcImg.WarpPerspective(homat2D, srcImg.Size());

                // 显示图像
                Cv2.ImShow("透视变换图像", dstImg);

                Cv2.WaitKey(0);
            }
        }

        /// <summary>
        /// 裁剪图像
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CropImage(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取图像

                Mat corpedImage = new Mat(mat, new OpenCvSharp.Rect(100, 100, 500, 500)); // 裁剪图像

                Cv2.ImShow("裁剪图像", corpedImage); // 显示图像
                Cv2.WaitKey(0); // 让图像弹窗等待关闭
            }
        }

        /// <summary>
        /// 裁剪图像2
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CropImage2(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取图像

                Mat corpedImage = new Mat(mat, new OpenCvSharp.Rect(100, 100, 500, 500)); // 裁剪图像

                // 百度骗我不好用
                //var rect = new OpenCvSharp.Rect(100, 100, 500, 500);
                //Mat img = mat.GetSubRect(rect);

                Cv2.ImShow("裁剪图像", corpedImage); // 显示图像
                Cv2.WaitKey(0); // 让图像弹窗等待关闭
            }
        }

        /// <summary>
        /// 拼接图像
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ConcatImage(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取图像

                // 水平方向的图片拼接
                Mat croppedImage = new Mat(mat, new OpenCvSharp.Rect(0, 0, 300, 300)); // 裁剪图像
                Mat croppedImage2 = new Mat(mat, new OpenCvSharp.Rect(300, 0, 300, 300)); // 裁剪图像
                Mat[] ImagesToConcat_H = { croppedImage, croppedImage2 };  // 要拼接的图像数组
                Mat concatenatedImage_H = new Mat(); // 拼接后的新图像
                Cv2.HConcat(ImagesToConcat_H, concatenatedImage_H); // Opencv的水平图像拼接。

                // 垂直方向的图片拼接
                Mat croppedImage3 = new Mat(mat, new OpenCvSharp.Rect(0, 0, 300, 300)); // 裁剪图像
                Mat croppedImage4 = new Mat(mat, new OpenCvSharp.Rect(0, 300, 300, 300)); // 裁剪图像
                Mat[] ImagesToConcat_V = { croppedImage3, croppedImage4 };  // 要拼接的图像数组
                Mat concatenatedImage_V = new Mat(); // 拼接后的新图像
                Cv2.VConcat(ImagesToConcat_V, concatenatedImage_V); // Opencv的垂直图像拼接。

                Cv2.ImShow("水平拼接图像", concatenatedImage_H); // 显示水平拼接后的图像
                Cv2.ImShow("垂直拼接图像", concatenatedImage_V); // 显示垂直拼接后的图像
                Cv2.WaitKey(0); // 等待图像显示
            }
        }

        /// <summary>
        /// 拆分合并功能
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SplitImage(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取图像

                Mat[] channels; // 单通道数组
                Cv2.Split(mat, out channels); // 将多通道图像拆分成单通道数组列表

                Cv2.ImShow("显示单通道图像", channels[0]); // 显示单通道图像
                                                    // Opencv的通道顺序使用的是BGR
                Mat[] channelsTomerge = { channels[1], channels[0], channels[2] }; // 打乱单通道顺序会产生和原图不一样效果
                Mat margedImage = new Mat(); // 合并后的多通道图像
                Cv2.Merge(channelsTomerge, margedImage); // 将单通道图像合并成多通道图像
                Cv2.ImShow("显示多通道图像", margedImage);

                Cv2.WaitKey(0);  // 等待图像显示
            }
        }

        /// <summary>
        /// 颜色转换
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ColorConver(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取图像

                Mat gray = new Mat(); // 要转换的灰度图
                Cv2.CvtColor(mat, gray, ColorConversionCodes.BGR2GRAY); // 将彩色图转换成单通道灰度图
                Cv2.CvtColor(mat, gray, ColorConversionCodes.BGR2GRAY); // 将BGR格式图像转换成HSV()格式-转成这种格式是为了图像分割-能够有效地分离颜色和亮度信息
                Cv2.ImShow("显示灰度图", gray); // 显示灰度图
                Cv2.WaitKey(0); // 等待图片显示
            }
        }

        /// <summary>
        /// 尺寸调整
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SizeImage(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取图像

                Mat result = new Mat(); // 调整尺寸后的图像
                Cv2.Resize(mat, result, new OpenCvSharp.Size(200, 200)); // 图像的尺寸调整
                Cv2.Resize(mat, result, new OpenCvSharp.Size(0, 0), 0.5, 0.5); // 按比例缩放，size要设置成0比例才生效，X轴比例，Y轴比例
                Cv2.ImShow("图像显示", result); // 显示图像
                Cv2.WaitKey(0); // 等待图像显示
            }
        }

        /// <summary>
        /// 图像二值化
        /// 图像二值化是去掉图像的背景色
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BinarizationImage(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取图像
                Mat gray = new Mat();
                Cv2.CvtColor(mat, gray, ColorConversionCodes.BGR2GRAY); // 图像转灰度图
                Scalar scalar = Cv2.Mean(gray); // 计算灰度图平均值
                                                // 这里灰度图的时候使用scalar.Val0，二通道使用Val1，三通道彩色图使用Val2，四通道有透明度的图像的使用Val3
                Cv2.Threshold(gray, gray, scalar.Val0, 255, ThresholdTypes.Binary); // 图像二值化 - 二值图的好处是去掉底色背景的

                ViewModel.BinarizationImage = gray.ToBitmapSource();

                //Cv2.ImShow("图像显示", gray); // 显示图像
                //Cv2.WaitKey(0); // 等待图像显示
            }
        }

        /// <summary>
        /// 与运算
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AndImage(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取图像
                Mat mat2 = new Mat(); // 第二个图像
                Mat result = new Mat(); // 输出图像
                mat2 = mat.Clone(); // 克隆图像
                mat2.SetTo(new Scalar(2, 2, 2)); // 设置所有通道颜色值都是2
                Cv2.BitwiseAnd(mat, mat2, result); // 图像与运算

                Cv2.ImShow("图像显示", result); // 显示图像
                Cv2.WaitKey(0); // 等待图像显示
            }
        }

        /// <summary>
        /// 或运算
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OrImage(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取图像
                Mat mat2 = new Mat(); // 第二个图像
                Mat result = new Mat(); // 输出图像
                mat2 = mat.Clone(); // 克隆图像
                mat2.SetTo(new Scalar(2, 2, 2)); // 设置所有通道颜色值都是2
                Cv2.BitwiseOr(mat, mat2, result); // 图像或运算

                Cv2.ImShow("图像显示", result); // 显示图像
                Cv2.WaitKey(0); // 等待图像显示
            }
        }

        /// <summary>
        /// 异或运算
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void XOrImage(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取图像
                Mat mat2 = new Mat(); // 第二个图像
                Mat result = new Mat(); // 输出图像
                mat2 = mat.Clone(); // 克隆图像
                mat2.SetTo(new Scalar(2, 2, 2)); // 设置所有通道颜色值都是2
                Cv2.BitwiseXor(mat, mat2, result); // 图像异或运算

                Cv2.ImShow("图像显示", result); // 显示图像
                Cv2.WaitKey(0); // 等待图像显示
            }
        }

        /// <summary>
        /// 取反运算
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void NotImage(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取图像
                Mat result = new Mat();
                Cv2.BitwiseNot(mat, result); // 图像取反运算-计算公式：255-当前颜色值=取反的颜色值  （取反相当于把颜色反过来）

                Cv2.ImShow("图像显示", result); // 显示图像
                Cv2.WaitKey(0); // 等待图像显示
            }
        }

        /// <summary>
        /// 两图相加
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ImageAdd(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取图像
                Mat mat2 = new Mat(); // 第二个图像
                Mat result = new Mat(); // 输出图像
                mat2 = mat.Clone(); // 克隆图像
                mat2.SetTo(new Scalar(2, 2, 2)); // 设置所有通道颜色值都是2
                Cv2.Add(mat, mat2, result); // 两图相加

                Cv2.ImShow("图像显示", result); // 显示图像
                Cv2.WaitKey(0); // 等待图像显示
            }
        }

        /// <summary>
        /// 减法运算
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ImageSubtract(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取图像
                Mat mat2 = new Mat(); // 第二个图像
                Mat result = new Mat(); // 输出图像
                mat2 = mat.Clone(); // 克隆图像
                mat2.SetTo(new Scalar(2, 2, 2)); // 设置所有通道颜色值都是2
                Cv2.Subtract(mat, mat2, result); // 两图相减

                Cv2.ImShow("图像显示", result); // 显示图像
                Cv2.WaitKey(0); // 等待图像显示
            }
        }

        /// <summary>
        /// 四则运算方法
        /// </summary>
        private void test()
        {
            if (ViewModel.MainImage != null)
            {
                // 原则：通道相同，尺寸相同，才能运算
                var mat = ViewModel.MainImage.ToMat(); // 第一个图像
                Mat mat2 = new Mat(); // 第二个图像
                Mat result = new Mat(); // 输出的图像
                Cv2.BitwiseAnd(mat, mat2, result);  // 与运算 - 
                Cv2.BitwiseNot(mat, result);  // 取反运算 - 计算公式：取反色值=255-当前色值
                Cv2.BitwiseOr(mat, mat2, result);  // 或运算
                Cv2.BitwiseXor(mat, mat2, result);  // 异或运算
                Cv2.Add(mat, mat2, result);  // 两图相加运算
                Cv2.Subtract(mat, mat2, result);  // 两图相减运算
                {  // 乘法运算-让图像跟接近255，让图像变得更白一点。
                    mat2 = mat.Clone(); // 克隆图像
                    mat2.SetTo(new Scalar(2, 2, 2)); // 设置所有通道颜色值都是2
                    Cv2.Multiply(mat, mat2, result);  // 乘法运算 把第一个图像的所有颜色值都乘以2
                }
                {  // 除法运算-让图像变得暗一点
                    mat2 = mat.Clone(); // 克隆图像
                    mat2.SetTo(new Scalar(2, 2, 2)); // 设置所有通道颜色值都是2
                    Cv2.Divide(mat, mat2, result);  // 除法运算
                }

                Cv2.ImShow("显示图像", result); // 显示图像
                Cv2.WaitKey(0); // 等待图像显示
            }
        }

        /// <summary>
        /// 四则运算
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TestImage(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                // 原则：通道相同，尺寸相同，才能运算
                var mat = ViewModel.MainImage.ToMat(); // 第一个图像
                Mat mat2 = new Mat(); // 第二个图像
                Mat result = new Mat(); // 输出的图像

                {  // 乘法运算
                    mat2 = mat.Clone(); // 克隆图像
                    mat2.SetTo(new Scalar(2, 2, 2)); // 设置所有通道颜色值都是2
                    Cv2.ImShow("显示图像1", mat2); // 显示图像
                    Cv2.Multiply(mat, mat2, result);  // 乘法运算 把第一个图像的所有颜色值都乘以2
                }

                Cv2.ImShow("显示图像", result); // 显示图像
                Cv2.WaitKey(0); // 等待图像显示
            }
        }

        /// <summary>
        /// 颜色分割-颜色分割的一种方法
        /// HSV格式图像用来做颜色分割
        /// 下面的代码实际原理没看懂-后期有机会再研究
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ColorSegmentation(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取图像
                Mat hsv = new Mat(); // HSV格式图像
                Mat result = new Mat(); // 输出的图像
                Cv2.CvtColor(mat, hsv, ColorConversionCodes.BGR2HSV); // 图像颜色转换为HSV格式
                                                                      //ViewModel.BinarizationImage = hsv.ToBitmapSource(); // 显示图像
                var mask = hsv.InRange(new Scalar(0, 46, 46), new Scalar(29, 255, 255)); // 红色区域
                                                                                         //ViewModel.GrayImage = mask.ToBitmapSource();
                var mask2 = hsv.InRange(new Scalar(165, 46, 46), new Scalar(180, 255, 255)); // 红色区域
                                                                                             //ViewModel.ResultImage = mask2.ToBitmapSource();
                Cv2.BitwiseOr(mask, mask2, result); // 范围叠加
                                                    //Cv2.ImShow("范围叠加图", result);
                Cv2.BitwiseNot(result, result); // 取反，黑白颜色反过来
                                                //Cv2.ImShow("取反图像", result);

                Mat outImg = new Mat(); // 输出图像
                Cv2.CvtColor(result, outImg, ColorConversionCodes.GRAY2BGR); // 灰度图转彩色图-将单通道图转成3通道图-此处只是转通道-图像没有变化
                Cv2.ImShow("灰度图转彩色图", outImg);
                Cv2.Add(mat, outImg, outImg); // 彩色图与原图相加，获取红色区域
                Cv2.ImShow("显示红色区域图像", outImg); // 显示红色区域图像

                Cv2.WaitKey(0); // 等待图像显示
            }
        }

        /// <summary>
        /// 直线检测方法
        /// 直线检测的一种方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void LineDetection(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取图像
                Mat gray = ImageHelper.ConvertToBlackBinary(mat); // 获取二值化图像
                ViewModel.BinarizationImage = gray.ToBitmapSource(); // 显示二值化图像
                                                                     // 比如5*5的区域腐蚀，腐蚀后变成3*3大小的区域-黑点变得越来越小，目的是为了把黑色的小点腐蚀掉。
                var kenal3 = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(2, 2)); // 二值图腐蚀方法，new OpenCvSharp.Size(2,2)设置腐蚀大小，用于去除黑色小颗粒
                Cv2.Dilate(gray, gray, kenal3); // 膨胀图像
                Cv2.ImShow("二值化Dilate", gray); // 显示膨胀图
                Cv2.Canny(gray, gray, 50, 150); // 找轮廓
                Cv2.ImShow("显示轮廓图", gray); // 显示轮廓图
                                           // Math.PI / 180:每一度检测一次-
                var lines = Cv2.HoughLinesP(gray, 1, Math.PI / 180, 100, 250, 50); // 霍夫直线检测
                if (lines.Length > 0)
                {
                    for (int i = 0; i < lines.Length; i++)
                    {  // 将检测到的直线在原图上画出来
                       // mat:原图-lines[i].P1, lines[i].P2：画直线的两个点-new Scalar(255,0,0)：颜色-1：画直线的粗细-LineTypes.AntiAlias：画直线类型
                        Cv2.Line(mat, lines[i].P1, lines[i].P2, new Scalar(255, 0, 0), 1, LineTypes.AntiAlias);
                    }
                }
                Cv2.ImShow("结果图", mat); // 结果图

            }
        }

        /// <summary>
        /// 计算灰度值
        /// 最大、最小、平均
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCalculateGray(object sender, RoutedEventArgs e)
        {
            if (ViewModel.MainImage != null)
            {
                var mat = ViewModel.MainImage.ToMat(); // 获取图像
                Mat gray = new Mat();
                Cv2.CvtColor(mat, gray, ColorConversionCodes.BGR2GRAY); // 图像转灰度图
                double maxVal = 0; // 初始化最大灰度值
                double minVal = 0; // 初始化最小灰度值
                Scalar scalar = Cv2.Mean(gray); // 计算灰度图平均值
                Cv2.MinMaxLoc(gray, out minVal, out maxVal);  // 计算最大灰度值
                ViewModel.BinarizationImage = gray.ToBitmapSource();
            }

        }

        /// <summary>
        /// 灰度化
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnGray(object sender, RoutedEventArgs e)
        {

        }

        /// <summary>
        /// 均值滤波
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnMeanImage(object sender, RoutedEventArgs e)
        {

        }

        /// <summary>
        /// 边缘检测
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCanny(object sender, RoutedEventArgs e)
        {

        }

        /// <summary>
        /// OpenCV坐标映射
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCoordinateMapping(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog(); // 打开文件对话框
            if (ofd.ShowDialog() == true)
            {
                var imgPath = ofd.FileName; // 获取文件路径

                Mat srcImg = Cv2.ImRead(imgPath); // 读取图像
                Cv2.ImShow("显示原图", srcImg);  // 显示原图
                //输出矩阵定义
                Mat result = new Mat(srcImg.Size(), srcImg.Type());

                //x方向与y方向矩阵
                Mat xMap = new Mat(srcImg.Size(), MatType.CV_32FC1);
                Mat yMap = new Mat(srcImg.Size(), MatType.CV_32FC1);
                //取图像的宽和高
                int rows = srcImg.Rows;
                int cols = srcImg.Cols;
                //图像遍历
                for (int j = 0; j < rows; j++)
                {
                    for (int i = 0; i < cols; i++)
                    {
                        //x与y均翻转
                        xMap.At<float>(j, i) = cols - i;
                        yMap.At<float>(j, i) = rows - j;
                    }
                }

                // 重映射操作
                // 参数
                // 参数InputArray src：输入变换前图像
                // 参数OutputArray dst：输出变换后图像，需要初始化一个空矩阵用来保存结果，不用设定矩阵尺寸
                // 参数xMap x方向矩阵
                // 参数yMap Y方向矩阵
                // 参数Linear 双线性插值（默认使用）
                Cv2.Remap(srcImg, result, xMap, yMap, InterpolationFlags.Linear);

                // 显示图像
                Cv2.ImShow("坐标映射图像", result);

                Cv2.WaitKey(0);
            }
        }

        /// <summary>
        /// 图像模版匹配
        /// c++代码的一种写法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnImageTemplateMatch(object sender, RoutedEventArgs e)
        {
            Mat srcImg = Cv2.ImRead(@"Resources/1.jpg"); //待搜索图像（大图）
            Mat templateImg = Cv2.ImRead(@"Resources/2.png"); // 搜索模板，需和原图一样数据类型且尺寸大小不能大于源图像

            Cv2.ImShow("搜索图像", srcImg); // 显示搜索结果映射图像

            int width = srcImg.Cols - templateImg.Cols + 1;//result宽度 - 搜索结果图像宽度
            int height = srcImg.Rows - templateImg.Rows + 1;//result高度 - 搜索结果图像高度

            // 32位无符号单通道
            Mat resultImg = new Mat(height, width, MatType.CV_32FC1);//创建结果映射图像 - 搜索结果图像
            // 搜索模板图像的接近图 - 进行模版匹配
            // 参数
            // srcImg:待搜索图像
            // templateImg:搜索模板
            // resultImg:映射图像
            // method:指定比较方法
            Cv2.MatchTemplate(srcImg, templateImg, resultImg, TemplateMatchModes.SqDiff); //平方差匹配法(最好匹配0) - 值越小匹配度越好
            //Cv2.MatchTemplate(srcImg, templateImg, resultImg, TemplateMatchModes.SqDiffNormed); //归一化平方差匹配法(最好匹配0)
            //Cv2.MatchTemplate(srcImg, templateImg, resultImg, TemplateMatchModes.CCorr); //相关匹配法(最坏匹配0)
            //Cv2.MatchTemplate(srcImg, templateImg, resultImg, TemplateMatchModes.CCorrNormed); //归一化相关匹配法(最坏匹配0)
            //Cv2.MatchTemplate(srcImg, templateImg, resultImg, TemplateMatchModes.CCoeff); //系数匹配法(最好匹配1)
            //Cv2.MatchTemplate(srcImg, templateImg, resultImg, TemplateMatchModes.CCoeffNormed);//化相关系数匹配,最佳值1
            Cv2.ImShow("结果映射图像", resultImg); // 显示搜索结果映射图像
            // 矩阵归一化 - 函数的作用是进行矩阵归一化。
            // 参数
            // resultImg：输入源图像，Mat类型
            // resultImg：输出结果图像，需要和原图一样的尺寸和类型
            // alpha：归一化后的最小值，默认为1
            // beta：归一化后的最大值，默认为0
            // normType：归一化类型，可选：NORM_INF, NORM_L1, NORM_L2（默认）等
            // dtype：默认值为-1，此参数为负值时，输出矩阵和src有同样的类型
            Cv2.Normalize(resultImg, resultImg, 0, 1, NormTypes.MinMax, -1);//归一化到0-1范围

            double minValue; // 最小值 - 匹配度最好的
            double maxValue; // 最大值 
            OpenCvSharp.Point minLoc; // 最小值 - 坐标
            OpenCvSharp.Point maxLoc; // 最大值 - 坐标
            // 寻找最值 - 函数的作用是在数组中找到全局最小值和最大值
            // 参数
            // resultImg：输入源图像，单通道图像
            // minValue：返回最小值，若无需返回，则置为0 - 值越小匹配度越好
            // maxValue：返回最大值，若无需返回，则置为0
            // minLoc：返回最小位置坐标，若无需返回，则置为0
            // maxLoc：返回最大位置坐标，若无需返回，则置为0
            Cv2.MinMaxLoc(resultImg, out minValue, out maxValue, out minLoc, out maxLoc);
            Debug.WriteLine($"minValue={minValue}");
            Debug.WriteLine($"maxValue={maxValue}");
            // 画矩形框
            // 参数
            // srcImg：待搜索图像（大图）
            // minLoc：最小位置坐标
            // pt2：模板的坐标
            // color：颜色
            // thickness：线条粗细
            // lineType：线条类型
            // shift：坐标的小数位数
            Cv2.Rectangle(srcImg, minLoc,new OpenCvSharp.Point(minLoc.X + templateImg.Cols, minLoc.Y + templateImg.Rows),new Scalar(0, 255, 0), 2, LineTypes.Link8, 0);
            Cv2.ImShow("显示模版匹配图像", srcImg);

            //Cv2.Rectangle(dst, maxLoc, Point(maxLoc.x + temp.cols, maxLoc.y + temp.rows), Scalar(0, 255, 0), 2, 8);
            //Cv2.ImShow("dst", dst);

            Cv2.WaitKey(0);
        }

        /// <summary>
        /// 图像模版匹配2
        /// c#代码的写法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnImageTemplateMatch2(object sender, RoutedEventArgs e)
        {

            // 加载图片和模板
            Mat image = Cv2.ImRead(@"Resources/1.jpg"); //待搜索图像（大图）
            Mat template = Cv2.ImRead(@"Resources/2.png"); // 搜索模板，需和原图一样数据类型且尺寸大小不能大于源图像
            // 获取模板的宽度和高度
            int templateWidth = template.Width;
            int templateHeight = template.Height;

            // 将图片和模板转换为灰度
            //Mat grayImage = image.CvtColor(ColorConversionCodes.BGR2GRAY);
            //Mat grayTemplate = template.CvtColor(ColorConversionCodes.BGR2GRAY);

            Mat result = new Mat(); // 搜索结果映射图像
            // 进行模板匹配
            // 参数
            // srcImg:待搜索图像
            // templateImg:搜索模板
            // resultImg:映射图像
            // method:指定比较方法
            //Cv2.MatchTemplate(grayImage, grayTemplate, result, TemplateMatchModes.CCoeffNormed);
            Cv2.MatchTemplate(image, template, result, TemplateMatchModes.CCoeffNormed);

            // 寻找最佳匹配位置
            OpenCvSharp.Point minLoc, maxLoc;
            double min, max;
            // 寻找最佳匹配位置
            // 参数
            // resultImg：输入源图像，单通道图像
            // minValue：返回最小值，若无需返回，则置为0 - 值越小匹配度越好
            // maxValue：返回最大值，若无需返回，则置为0
            // minLoc：返回最小位置坐标，若无需返回，则置为0
            // maxLoc：返回最大位置坐标，若无需返回，则置为0
            Cv2.MinMaxLoc(result, out min, out max, out minLoc, out maxLoc);

            // 画出匹配区域
            OpenCvSharp.Rect matchLoc = new OpenCvSharp.Rect(maxLoc, new OpenCvSharp.Size(templateWidth, templateHeight));
            // 画矩形框
            // 参数
            // srcImg：待搜索图像（大图）
            //matchLoc: 匹配区域矩形
            // color：颜色
            // thickness：线条粗细
            Cv2.Rectangle(image, matchLoc, new Scalar(0, 255, 0), 2);

            // 显示图片
            Cv2.ImShow("显示模版匹配图像", image);
            Cv2.WaitKey(0);

            // 释放资源
            //grayImage.Dispose();
            //grayTemplate.Dispose();
            image.Dispose();
            template.Dispose();
            result.Dispose();
        }
    }
}
